package com.purchase.ml.service.sys.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.purchase.ml.bean.page.Page;
import com.purchase.ml.bean.req.mail.MailDto;
import com.purchase.ml.bean.req.mail.MailMessage;
import com.purchase.ml.bean.req.mail.RePwdDto;
import com.purchase.ml.bean.req.sys.*;
import com.purchase.ml.bean.res.sys.SysRes;
import com.purchase.ml.config.cache.CacheDao;
import com.purchase.ml.consts.CommonConsts;
import com.purchase.ml.consts.Constants;
import com.purchase.ml.consts.MailConstants;
import com.purchase.ml.entity.acc.SysUser;
import com.purchase.ml.entity.sys.SysPermission;
import com.purchase.ml.entity.sys.SysRoleUser;
import com.purchase.ml.enums.RedisCacheEnum;
import com.purchase.ml.enums.RetStatusNum;
import com.purchase.ml.exception.BizException;
import com.purchase.ml.exception.CommonEnum;
import com.purchase.ml.mapper.sys.SysUserMapper;
import com.purchase.ml.mapper.sys.SysPermissionMapper;
import com.purchase.ml.mapper.sys.SysRoleMapper;
import com.purchase.ml.mapper.sys.SysRoleUserMapper;
import com.purchase.ml.security.JwtUtil;
import com.purchase.ml.service.email.user.UserEmail;
import com.purchase.ml.service.sys.SysUserService;
import com.purchase.ml.utils.MD5;
import com.purchase.ml.utils.RedisClient;
import com.purchase.ml.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.purchase.ml.consts.CommonConsts.DEFAULT_PWD;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author kyyang
 * Created 2021-01-05
 */
@Service
@Slf4j
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> implements SysUserService {
    @Autowired
    private CacheDao cacheDao;
    @Value("${jwt.token.expire.time}")
    private Long tokenExpireTime;

    @Autowired
    private SysUserMapper sysUserMapper;

    @Autowired
    private SysRoleUserMapper sysRoleUserMapper;

    @Autowired
    private SysRoleMapper sysRoleMapper;

    @Autowired
    private SysPermissionMapper sysPermissionMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private UserEmail userEmail;

    @Override
    public boolean refreshTokenIsValid(String oldToken) {
        SysUser userByToken = JwtUtil.getUserByToken(oldToken);
        String refreshTokenTime = (String) cacheDao.hget(RedisCacheEnum.REDIS_TOKEN_CACHE.getCode(), CommonConsts.USER_REDIS_KEY+userByToken.getAcc()+userByToken.getSalt());
        if (refreshTokenTime == null) {
            return false;
        }
        JSONObject jsonObject = JSON.parseObject(refreshTokenTime);
        Long expire = jsonObject.getLong("expire");
        String token = jsonObject.getString("token");
        if (!oldToken.equals(token)) {
            return false;
        }
        return System.currentTimeMillis() <= expire;
    }

    @Override
    public String loginForToken(SysUser user) {
        //获取用户token值
        String token = JwtUtil.sign(user, tokenExpireTime * 1000);
        //将token作为RefreshToken Key 存到缓存中，缓存时间为token有效期的两倍
        String refreshTokenCacheKey = token;
        Date expireDate = new Date(System.currentTimeMillis() + tokenExpireTime * 2 * 1000);
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("token", refreshTokenCacheKey);
        jsonObject.put("expire", expireDate.getTime());
        cacheDao.hset(RedisCacheEnum.REDIS_TOKEN_CACHE.getCode(), CommonConsts.USER_REDIS_KEY+user.getAcc()+user.getSalt(), jsonObject.toJSONString());
        log.info("token:{}", token);
        return token;
    }

    @Override
    public SysUser findByAccount(String userName) {
        return sysUserMapper.selectByAccount(userName);
    }

    @Override
    public SysUser getById(Long userId) {
        return sysUserMapper.selectById(userId);
    }

    @Override
    @Transactional
    public int save(SysUserDto sysUserDto) {
        SysUser sysUser = new SysUser();
        //默认密码  账号+账号后两位
        sysUser.setAcc(sysUserDto.getAcc());
        Integer state = sysUserDto.getState()==null? Constants.NORMAL_STATE:sysUserDto.getState();
        sysUser.setState(state);
        sysUser.setCreateAt(new Date());
        sysUser.setName(sysUserDto.getName());
        sysUser.setSalt(StringUtils.getUUID());
        sysUser.setDealerId(sysUserDto.getDealerId());
        sysUser.setRoleType(sysUserDto.getRoleType());
        sysUser.setHeadUrl(sysUserDto.getHeadUrl());
        sysUser.setEmail(sysUserDto.getEmail());
        sysUser.setIsAdmin(sysUserDto.getIsAdmin());
        String registerPwd = sysUserDto.getPwd()==null?DEFAULT_PWD:sysUserDto.getPwd();
        sysUser.setPwd(MD5.md5(registerPwd, sysUser.getSalt()));
        sysUserMapper.insert(sysUser);
        //获取该用户ID
        SysUser ml = sysUserMapper.selectByAccount(sysUserDto.getAcc());
        Integer roleId = sysUserDto.getRoleType();
        SysRoleUser ent = new SysRoleUser();
        ent.setUserId(ml.getId());
        ent.setRoleId(roleId.longValue());
        int isAdd = sysRoleUserMapper.insert(ent);
        return isAdd;
    }

    @Override
    public Page<SysRes> selectAccByPage(SysUserQueryDto accUserQueryDto) {
        QueryWrapper<SysUser> wrapper = new QueryWrapper<>();
        if (org.apache.commons.lang3.StringUtils.isNotBlank(accUserQueryDto.getAcc())) {
            wrapper.like("acc", accUserQueryDto.getAcc());
        }
        if (accUserQueryDto.getDealerId() !=null) {
            wrapper.eq("dealerId", accUserQueryDto.getDealerId());
        }
        Integer count = sysUserMapper.selectCount(wrapper);
        Page<SysRes> result = new Page<>(accUserQueryDto.getPageIndex(),accUserQueryDto.getPageNum());
        if(count<1){
            return result;
        }
        result.setTotal(count);
        List<SysRes> list = sysUserMapper.selectAccPages(accUserQueryDto);
        result.setRecords(list);
        log.info("======result===={}",result);
        return result;
    }

    @Override
    @Transactional
    public int editInfo(SysUserEditDto sysUserEditDto) {
        SysUser sysUser = new SysUser();
        sysUser.setUpdateAt(new Date());
        UpdateWrapper<SysUser> updateWrapper = new UpdateWrapper<SysUser>();

        if (org.apache.commons.lang3.StringUtils.isNotBlank(sysUserEditDto.getHeadUrl())) {
            updateWrapper.set("head_url", sysUserEditDto.getHeadUrl());
        }

        if (org.apache.commons.lang3.StringUtils.isNotBlank(sysUserEditDto.getName())) {
            updateWrapper.set("name", sysUserEditDto.getName());
        }
        if (sysUserEditDto.getMerchantId()!=null) {
            updateWrapper.set("merchant_id", sysUserEditDto.getMerchantId());
        }
        if (org.apache.commons.lang3.StringUtils.isNotBlank(sysUserEditDto.getTel())) {
            updateWrapper.set("tel", sysUserEditDto.getTel());
        }
        if (org.apache.commons.lang3.StringUtils.isNotBlank(sysUserEditDto.getState())) {
            updateWrapper.set("state", sysUserEditDto.getState());
        }

        updateWrapper.eq("id", sysUserEditDto.getId().intValue());
        int update = sysUserMapper.update(sysUser, updateWrapper);
        return update;
    }

    @Override
    @Transactional
    public int editResetpwd(SysUser sysUser) {
        sysUser.setUpdateAt(new Date());
        sysUser.setPwd(MD5.md5(DEFAULT_PWD, sysUser.getSalt()));
        int i = sysUserMapper.updateById(sysUser);
        return i;
    }

    @Override
    @Transactional
    public int editPwd(SysUser byAccount, SysUserEditPwdDto sysUserEditPwdDto) {
        byAccount.setUpdateAt(new Date());
        String s = MD5.md5(sysUserEditPwdDto.getNewpwd(), byAccount.getSalt());
        byAccount.setPwd(s);
        int i = sysUserMapper.updateById(byAccount);
        return i;
    }

    @Override
    public void logout() {
        SysUser sysUser = JwtUtil.getCurrentUser();
        cacheDao.del(sysUser.getAcc()+sysUser.getSalt());
    }

    @Override
    public List<SysPermission> accountPermissionsQuery(SysPermissionsDto sysPermissionsDto) {
        Integer permissionId = sysPermissionsDto.getPermissionId();
        if(permissionId==null){
            permissionId=0;
        }
        List<SysPermission> list = sysUserMapper.selectCurrentPer(JwtUtil.getUserId(),permissionId);
        return list;
    }

    @Override
    public List<SysPermission> accountMenuQuery() {
        List<SysPermission> list = sysUserMapper.selectCurrentMenu(JwtUtil.getUserId());
        list.parallelStream().forEach((per) -> {
            List<SysPermission> child = list.stream().filter((p) -> {
                return p.getParentId().equals(per.getId());
            }).collect(Collectors.toList());
            per.setChild(child);
        });
        return list.stream().filter((p) -> {
            return p.getParentId()==0;
        }).collect(Collectors.toList());
    }

    @Override
    public SysUser getUserByEmail(String email) {
        LambdaQueryWrapper<SysUser> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(SysUser::getEmail,email);
        List<SysUser> userList = sysUserMapper.selectList(wrapper);
        if(userList == null && userList.size()!=0){
            throw new BizException(CommonEnum.USER_MAIL_NO_EXIST_ERROR);
        }
        return userList.get(0);
    }

    @Override
    public Boolean handleResetPwd(MailDto dto) {
        SysUser sysUser = sysUserMapper.selectByAccount(dto.getEmail());
        if(sysUser == null){
            return false;
        }
        // 生成校验码
        String code = getValidate(4);
        // 1、构建消息模板
        MailMessage mailMessage = userEmail.getPwdReset(dto,code);
        mailMessage.setCode(code);
        // 2、将验证码放入redis中
        cacheDao.hset(RedisCacheEnum.RESET_PWD_CODE.getCode(),dto.getEmail(),code);
//        redisTemplate.opsForValue().set(MailConstants.RESET_PWD_CODE+dto.getEmail(),code,30, TimeUnit.MINUTES);
        String message = JSON.toJSONString(mailMessage);
        RedisClient.lpush(MailConstants.REDIS_MAIL_MESSAGE,message);
        return true;
    }

    @Override
    public Boolean verifyRepwdCode(RePwdDto dto) {
//        String redisCode = (String) redisTemplate.opsForValue().get(MailConstants.RESET_PWD_CODE + dto.getEmail());
        String redisCode = cacheDao.hget(RedisCacheEnum.RESET_PWD_CODE.getCode(),dto.getEmail(), String.class);
        cacheDao.del(dto.getEmail());
        if(!dto.getCode().equals(redisCode) || redisCode == null ){
           return false;
        }

        return true;
    }

    /**
     * 生成验证码
     * @param i 验证码长度
     * @return
     */
    private static String getValidate(int i) {
        Random random = new Random();
        String num = random.nextInt(10000) + "";
        // 这一步很关键，将生成的num赋值给另一个字符串，用以当做固定值。
        String finalNum = num;
        if (num.length() < i) {
            for (int j = 1; j <= i - finalNum.length(); j++) {
                num = "0" + num;  // 做字符串的拼接。不足4位前面补0
            }
        }
        return num;
    }
}
